// Fill out your copyright notice in the Description page of Project Settings.


#include "AbilitySystem/AuraAbilitySystemBPLibrary.h"

#include "AbilitySystemComponent.h"
#include "AbilitySystem/AuraAbilityTypes.h"
#include "Game/AuraGameModeBase.h"
#include "GameFramework/GameModeBase.h"
#include "Interaction/CombatInterface.h"
#include "Kismet/GameplayStatics.h"
#include "Player/AuraHUD.h"
#include "Player/AuraPlayerState.h"

UOverlayWidgetController* UAuraAbilitySystemBPLibrary::GetOverWidgetController(const UObject* WorldContextObject)
{
	if (APlayerController* PC = UGameplayStatics::GetPlayerController(WorldContextObject, 0))
	{
		if (AAuraHUD* AuraHUD = Cast<AAuraHUD>(PC->GetHUD()))
		{
			AAuraPlayerState* PS = PC->GetPlayerState<AAuraPlayerState>();
			UAttributeSet* AS = PS->GetAttributeSet();
			UAbilitySystemComponent* ASC = PS->GetAbilitySystemComponent();
			const FWidgetControllerProps WidgetControllerProps = FWidgetControllerProps(PC, PS, ASC, AS);
			return AuraHUD->GetOverlayWidgetController(WidgetControllerProps);
		}
	}
	return nullptr;
}

UAttributeMenuWidgetController* UAuraAbilitySystemBPLibrary::GetAttributeMenuWidgetController(
	const UObject* WorldContextObject
)
{
	if (APlayerController* PC = UGameplayStatics::GetPlayerController(WorldContextObject, 0))
	{
		if (AAuraHUD* AuraHUD = Cast<AAuraHUD>(PC->GetHUD()))
		{
			AAuraPlayerState* PS = PC->GetPlayerState<AAuraPlayerState>();
			UAttributeSet* AS = PS->GetAttributeSet();
			UAbilitySystemComponent* ASC = PS->GetAbilitySystemComponent();
			const FWidgetControllerProps WidgetControllerProps = FWidgetControllerProps(PC, PS, ASC, AS);
			return AuraHUD->GetAttributeMenuWidgetController(WidgetControllerProps);
		}
	}
	return nullptr;
}

void UAuraAbilitySystemBPLibrary::InitDefaultAttributes(
	const UObject* WorldContextObject, const ERoleClass RoleClass, const float Level, UAbilitySystemComponent* ASC
)
{
	AAuraGameModeBase* GameModeBase = Cast<AAuraGameModeBase>(UGameplayStatics::GetGameMode(WorldContextObject));
	// if (GameModeBase == nullptr)
	// {
	// 	return;
	// }

	// 从游戏模式中获取角色类信息
	const UCharacterClassInfo* ClassInfo = GameModeBase->CharacterClassInfo;
	// 通过职业获取角色类默认信息
	const FCharacterClassDefaultInfo ClassDefaultInfo = ClassInfo->GetClassDefaultInfo(RoleClass);

	//应用基础属性
	FGameplayEffectContextHandle PrimaryContextHandle = ASC->MakeEffectContext();
	PrimaryContextHandle.AddSourceObject(WorldContextObject);
	const FGameplayEffectSpecHandle PrimarySpecHandle = ASC->MakeOutgoingSpec(
		ClassDefaultInfo.PrimaryAttributes,
		Level,
		PrimaryContextHandle
	);
	ASC->ApplyGameplayEffectSpecToSelf(*PrimarySpecHandle.Data.Get());

	//设置次级属性
	FGameplayEffectContextHandle SecondaryContextHandle = ASC->MakeEffectContext();
	SecondaryContextHandle.AddSourceObject(WorldContextObject);
	const FGameplayEffectSpecHandle SecondarySpecHandle = ASC->MakeOutgoingSpec(
		ClassInfo->SecondaryAttributes,
		Level,
		SecondaryContextHandle
	);
	ASC->ApplyGameplayEffectSpecToSelf(*SecondarySpecHandle.Data.Get());

	//填充血量和蓝量
	FGameplayEffectContextHandle VitalContextHandle = ASC->MakeEffectContext();
	VitalContextHandle.AddSourceObject(WorldContextObject);
	const FGameplayEffectSpecHandle VitalSpecHandle = ASC->MakeOutgoingSpec(
		ClassInfo->VitalAttributes,
		Level,
		VitalContextHandle
	);
	ASC->ApplyGameplayEffectSpecToSelf(*VitalSpecHandle.Data.Get());
}

void UAuraAbilitySystemBPLibrary::GiveStartupAbilities(
	const UObject* WorldContextObject, UAbilitySystemComponent* ASC, ERoleClass RoleClass
)
{
	// 从GameMode获取到关卡角色的配置
	UCharacterClassInfo* CharacterClassDefaultInfo = GetCharacterClassDefaultInfo(WorldContextObject);
	if (CharacterClassDefaultInfo == nullptr)
	{
		return;
	}

	// 从战斗接口获取到角色的等级
	ICombatInterface* CombatInterface = Cast<ICombatInterface>(ASC->GetAvatarActor());
	int32 CharacterLevel = 1;
	if (CombatInterface)
	{
		CharacterLevel = CombatInterface->GetLevel();
	}

	// 授予角色通用技能数组
	for (TSubclassOf<UGameplayAbility> CommonAbility : CharacterClassDefaultInfo->CommonAbilities)
	{
		FGameplayAbilitySpec AbilitySpec = FGameplayAbilitySpec(CommonAbility, CharacterLevel);
		ASC->GiveAbility(AbilitySpec);
	}

	// 授予角色职业技能数组
	FCharacterClassDefaultInfo ClassDefaultInfo = CharacterClassDefaultInfo->GetClassDefaultInfo(RoleClass);

	for (TSubclassOf<UGameplayAbility> StartupAbility : ClassDefaultInfo.StartupAbilities)
	{
		FGameplayAbilitySpec AbilitySpec = FGameplayAbilitySpec(StartupAbility, CharacterLevel);
		ASC->GiveAbility(AbilitySpec);
	}
}

bool UAuraAbilitySystemBPLibrary::IsBlockedHit(const FGameplayEffectContextHandle& EffectContextHandle)
{
	if (const FAuraGameplayEffectContext* AuraEffectContext = static_cast<const FAuraGameplayEffectContext*>(
		EffectContextHandle.Get()))
	{
		return AuraEffectContext->IsBlockedHit();
	}

	return false;
}

bool UAuraAbilitySystemBPLibrary::IsCriticalHit(const FGameplayEffectContextHandle& EffectContextHandle)
{
	if (const FAuraGameplayEffectContext* AuraEffectContext = static_cast<const FAuraGameplayEffectContext*>(
		EffectContextHandle.Get()))
	{
		return AuraEffectContext->IsCriticalHit();
	}

	return false;
}

void UAuraAbilitySystemBPLibrary::SetIsBlockedHit(
	FGameplayEffectContextHandle& EffectContextHandle, const bool InbIsBlockedHit
)
{
	FAuraGameplayEffectContext* AuraEffectContext = static_cast<FAuraGameplayEffectContext*>(EffectContextHandle.Get());
	AuraEffectContext->SetIsBlockedHit(InbIsBlockedHit);
}

void UAuraAbilitySystemBPLibrary::SetIsCriticalHit(
	FGameplayEffectContextHandle& EffectContextHandle, const bool InbIsCriticalHit
)
{
	FAuraGameplayEffectContext* AuraEffectContext = static_cast<FAuraGameplayEffectContext*>(EffectContextHandle.Get());
	AuraEffectContext->SetIsCriticalHit(InbIsCriticalHit);
}

void UAuraAbilitySystemBPLibrary::GetLivePlayersWithinRadius(
	const UObject* WorldContextObject, TArray<AActor*>& OutOverlappingActors, const TArray<AActor*>& ActorsToIgnore,
	const FVector& Origin, const float Radius
)
{
	//创建一个碰撞查询的配置
	FCollisionQueryParams SphereParams;
	// 添加忽略的Actor
	SphereParams.AddIgnoredActors(ActorsToIgnore);

	TArray<FOverlapResult> OverlapResults;
	if (UWorld* World = GEngine->GetWorldFromContextObject(WorldContextObject, EGetWorldErrorMode::LogAndReturnNull))
	//获取当前所处的场景，如果获取失败，将打印并返回Null
	{
		// //获取到所有与此球体碰撞的动态物体
		World->OverlapMultiByObjectType(
			OverlapResults,
			Origin,
			FQuat::Identity,
			FCollisionObjectQueryParams(FCollisionObjectQueryParams::InitType::AllDynamicObjects),
			FCollisionShape::MakeSphere(Radius),
			SphereParams
		);

		// 遍历所有获取到的动态Actor
		for (FOverlapResult& OverlapResult : OverlapResults)
		{
			// 判断当前Actor是否包含战斗接口
			bool bImplementInterface = OverlapResult.GetActor()->Implements<UCombatInterface>();
			// 判断当前Actor是否存活，如果不包含战斗接口，将不会判断存活
			if (bImplementInterface && !ICombatInterface::Execute_IsDead(OverlapResult.GetActor()))
			{
				//将Actor添加到返回数组，AddUnique 只有在此Actor未被添加时，才可以添加到数组
				OutOverlappingActors.AddUnique(OverlapResult.GetActor());
			}
		}
	}
}

bool UAuraAbilitySystemBPLibrary::IsFriendly(const AActor* ActorA, const AActor* ActorB)
{
	if (ActorA->ActorHasTag("Player"))
	{
		return ActorB->ActorHasTag("Player");
	}

	if (ActorA->ActorHasTag("Enemy"))
	{
		return ActorB->ActorHasTag("Enemy");
	}
	
	return false;
}

int32 UAuraAbilitySystemBPLibrary::GetXPRewardForClassAndLevel(
	const UObject* WorldContextObject, ERoleClass RoleClass, int32 Level
)
{
	//从实例获取到关卡角色的配置
	UCharacterClassInfo* CharacterClassDefaultInfo = GetCharacterClassDefaultInfo(WorldContextObject);
	if (CharacterClassDefaultInfo == nullptr)
	{
		return 0;
	}

	//获取到默认的基础角色数据
	const FCharacterClassDefaultInfo& ClassDefaultInfo = CharacterClassDefaultInfo->GetClassDefaultInfo(RoleClass);

	float XPReward = ClassDefaultInfo.XPReward.GetValueAtLevel(Level);

	return static_cast<int32>(XPReward);
}

UCharacterClassInfo* UAuraAbilitySystemBPLibrary::GetCharacterClassDefaultInfo(const UObject* WorldContextObject)
{
	//获取到当前关卡的GameMode实例
	AAuraGameModeBase* GameModeBase = Cast<AAuraGameModeBase>(UGameplayStatics::GetGameMode(WorldContextObject));

	// if (GameModeBase == nullptr)
	// {
	// 	return nullptr;
	// }

	//返回关卡的角色的配置
	return GameModeBase->CharacterClassInfo;
}
